﻿using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Diagnostics;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class InternalAttributeAnalyzer : DiagnosticAnalyzer {

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(InternalAttributeAnalyzer)}] {message}");
    }

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        => ImmutableArray.Create(InternalAttributeAccessDiagnostic.Rule);

    public override void Initialize(AnalysisContext context) {
        context.EnableConcurrentExecution();
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.RegisterSyntaxNodeAction(AnalyzeAccess, SyntaxKind.SimpleMemberAccessExpression);
        context.RegisterSyntaxNodeAction(AnalyzeAccess, SyntaxKind.IdentifierName);
    }

    /// <summary>
    /// 分析成员访问表达式（如 obj.fieldName）
    /// </summary>
    private void AnalyzeAccess(SyntaxNodeAnalysisContext context) {
        var compilation = context.Compilation;
        var assembly = compilation.Assembly;
        if (assembly.IsIgnore()) {
            return;
        }

        var internalAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.Internal);
        if (internalAttributeSymbol == null) {
            return;
        }

        var skipInternalValidationAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.SkipInternalValidation);
        if (skipInternalValidationAttributeSymbol == null) {
            return;
        }

        var accessedSymbol = context.Node switch {
            MemberAccessExpressionSyntax memberAccess => context.SemanticModel.GetSymbolInfo(memberAccess).Symbol,
            IdentifierNameSyntax identifier => context.SemanticModel.GetSymbolInfo(identifier).Symbol,
            _ => null
        };

        if (accessedSymbol == null) {
            return;
        }

        if (accessedSymbol.Kind is not SymbolKind.Field and not SymbolKind.Property and not SymbolKind.Method) {
            return;
        }

        var isMarkedWithInternal = accessedSymbol.GetAttributes(internalAttributeSymbol).Any();
        if (isMarkedWithInternal) {
            var typeDeclaration = context.Node.FirstAncestorOrSelf<TypeDeclarationSyntax>();
            if (typeDeclaration != null) {
                var typeSymbol = context.SemanticModel.GetDeclaredSymbol(typeDeclaration);
                if (typeSymbol != null) {
                    var hasInternalIgnore = typeSymbol.GetAttributes(skipInternalValidationAttributeSymbol).Any();
                    if (hasInternalIgnore) {
                        return;
                    }
                }
            }
            context.ReportDiagnostic(Diagnostic.Create(
                InternalAttributeAccessDiagnostic.Rule,
                context.Node.GetLocation()
            ));
        }
    }
}
